package com.linkedin.datahub.graphql.types.module;

import static com.linkedin.datahub.graphql.authorization.AuthorizationUtils.canView;
import static com.linkedin.metadata.Constants.DATAHUB_PAGE_MODULE_PROPERTIES_ASPECT_NAME;

import com.linkedin.common.urn.Urn;
import com.linkedin.data.DataMap;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
import com.linkedin.datahub.graphql.generated.*;
import com.linkedin.datahub.graphql.types.common.mappers.util.MappingHelper;
import com.linkedin.datahub.graphql.types.mappers.MapperUtils;
import com.linkedin.datahub.graphql.types.mappers.ModelMapper;
import com.linkedin.entity.EntityResponse;
import com.linkedin.entity.EnvelopedAspectMap;
import com.linkedin.module.DataHubPageModuleProperties;
import java.time.Instant;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class PageModuleMapper implements ModelMapper<EntityResponse, DataHubPageModule> {

  public static final PageModuleMapper INSTANCE = new PageModuleMapper();

  public static DataHubPageModule map(
      @Nullable final QueryContext context, @Nonnull final EntityResponse entityResponse) {
    return INSTANCE.apply(context, entityResponse);
  }

  @Override
  public DataHubPageModule apply(
      @Nullable final QueryContext context, @Nonnull final EntityResponse entityResponse) {
    final DataHubPageModule result = new DataHubPageModule();
    Urn entityUrn = entityResponse.getUrn();

    result.setUrn(entityResponse.getUrn().toString());
    result.setType(EntityType.DATAHUB_PAGE_MODULE);

    setDefaultModule(result);

    EnvelopedAspectMap aspectMap = entityResponse.getAspects();
    MappingHelper<DataHubPageModule> mappingHelper = new MappingHelper<>(aspectMap, result);
    mappingHelper.mapToResult(
        DATAHUB_PAGE_MODULE_PROPERTIES_ASPECT_NAME,
        (module, dataMap) -> mapProperties(module, dataMap));

    if (context != null && !canView(context.getOperationContext(), entityUrn)) {
      return AuthorizationUtils.restrictEntity(result, DataHubPageModule.class);
    } else {
      return result;
    }
  }

  private void mapProperties(@Nonnull DataHubPageModule module, @Nonnull DataMap dataMap) {
    DataHubPageModuleProperties gmsModuleProperties = new DataHubPageModuleProperties(dataMap);
    com.linkedin.datahub.graphql.generated.DataHubPageModuleProperties properties =
        new com.linkedin.datahub.graphql.generated.DataHubPageModuleProperties();

    if (gmsModuleProperties.hasName()) {
      properties.setName(gmsModuleProperties.getName());
    }

    if (gmsModuleProperties.hasType()) {
      properties.setType(PageModuleTypeMapper.map(gmsModuleProperties.getType()));
    }

    if (gmsModuleProperties.hasVisibility()) {
      properties.setVisibility(PageModuleVisibilityMapper.map(gmsModuleProperties.getVisibility()));
    }

    if (gmsModuleProperties.hasParams()) {
      properties.setParams(PageModuleParamsMapper.map(gmsModuleProperties.getParams()));
    }

    if (gmsModuleProperties.hasCreated()) {
      properties.setCreated(MapperUtils.createResolvedAuditStamp(gmsModuleProperties.getCreated()));
    }

    if (gmsModuleProperties.hasLastModified()) {
      properties.setLastModified(
          MapperUtils.createResolvedAuditStamp(gmsModuleProperties.getLastModified()));
    }

    module.setProperties(properties);
  }

  /*
   * In the case that a module is deleted and the references haven't been cleaned up yet (this process is async)
   * set a default module to prevent APIs breaking. The UI queries for whether the entity exists and it will
   * be filtered out.
   */
  private void setDefaultModule(final DataHubPageModule result) {
    com.linkedin.datahub.graphql.generated.DataHubPageModuleProperties properties =
        new com.linkedin.datahub.graphql.generated.DataHubPageModuleProperties();
    properties.setName("");
    properties.setType(DataHubPageModuleType.OWNED_ASSETS);
    properties.setParams(new DataHubPageModuleParams());

    DataHubPageModuleVisibility visibility = new DataHubPageModuleVisibility();
    visibility.setScope(PageModuleScope.GLOBAL);
    properties.setVisibility(visibility);

    ResolvedAuditStamp auditStamp = new ResolvedAuditStamp();
    auditStamp.setTime(Instant.now().toEpochMilli());
    auditStamp.setActor(new CorpUser());
    properties.setCreated(auditStamp);
    properties.setLastModified(auditStamp);

    result.setProperties(properties);
  }
}
